package com.podigua.plugins;

import com.podigua.plugins.utils.FileUtils;
import com.podigua.plugins.utils.StringUtils;
import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.lang3.SystemUtils;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.*;
import org.apache.maven.project.MavenProject;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * 打包
 *
 * @author podigua
 * @date 2024/03/04
 */
@Mojo(name = "package", requiresDependencyResolution = ResolutionScope.RUNTIME)
@Execute(phase = LifecyclePhase.PACKAGE)
public class JPackageMojo extends AbstractMojo {
    private final static String JAVAFX = "javafx";
    private final static String WINDOWS = "windows";
    private final static String MAC = "mac";
    private final static String LINUX = "linux";
    private final static String LIB = "lib";
    /**
     * 项目
     */
    @Parameter(defaultValue = "${project}")
    private MavenProject project;

    private String workDirectory;
    /**
     * 资源目录
     */
    private String resourceDirectory;

    /**
     * 主jar
     */
    @Parameter(required = true)
    private String mainClass;

    /**
     * 主jar
     */
    private String mainJar;
    /**
     * image 路径
     */
    @Parameter
    private String imagePath;

    /**
     * <p>
     * 传递给｛@code可执行文件｝的vm选项列表。
     * </p>
     */
    @Parameter
    private List<String> options;

    /**
     * 为执行的程序用空格分隔的参数。数组
     */
    @Parameter
    private List<String> arguments;

    /**
     * 类型
     * {"app-image", "exe", "msi", "rpm", "deb", "pkg", "dmg"}
     */
    @Parameter
    private String type;
    /**
     * 版本
     */
    @Parameter(defaultValue = "${project.version}")
    private String appVersion;
    /**
     * 版权
     */
    @Parameter
    private String copyright;

    /**
     * 描述
     */
    @Parameter
    private String description;

    /**
     * 图标
     */
    @Parameter
    private String icon;


    /**
     * 名称
     */
    @Parameter
    private String name;
    /**
     * 厂商
     */
    @Parameter
    private String vendor;
    /**
     * 启用详细输出
     */
    @Parameter(defaultValue = "false")
    private Boolean verbose;

    /**
     * MAC 软件包标识符
     */
    @Parameter
    private String macPackageIdentifier;
    /**
     * MAC 软件包名称
     * 显示在菜单栏中的应用程序的名称,可能与应用程序名称不同。此名称的长度必须小于16个字符，并且适合在菜单栏和应用程序信息窗口中显示。默认为应用程序名称。
     */
    @Parameter
    private String macPackageName;
    /**
     * MAC 软件包签名前缀
     */
    @Parameter
    private String macPackageSigningPrefix;
    /**
     * MAC 标志
     */
    @Parameter
    private String macSign;
    /**
     * Mac 签名钥匙串
     */
    @Parameter
    private String macSigningKeychain;
    /**
     * Mac 签名钥匙串
     */
    @Parameter
    private String macSigningKeyUserName;
    /**
     * Mac 应用商店
     */
    @Parameter(defaultValue = "false")
    private Boolean macAppStore;
    /**
     * Mac 权利
     */
    @Parameter
    private String macEntitlements;
    /**
     * Mac 应用类别
     */
    @Parameter
    private String macAppCategory;

    /**
     * 关于网址
     */
    @Parameter
    private String aboutUrl;

    /**
     * 目录的绝对路径
     */
    @Parameter
    private String installDir;

    /**
     * 为应用程序创建控制台启动器，应指定需要控制台交互的应用程序
     */
    @Parameter(defaultValue = "false")
    private Boolean winConsole;

    /**
     * 添加对话框，以便用户选择安装产品的目录
     */
    @Parameter(defaultValue = "false")
    private Boolean winDirChooser;

    /**
     * 用户可以获取更多信息或技术支持的URL
     */
    @Parameter
    private String winHelpUrl;

    /**
     * 请求为此应用程序添加开始菜单快捷方式
     */
    @Parameter(defaultValue = "false")
    private Boolean winMenu;

    /**
     * 将此应用程序放置在的开始菜单组
     */
    @Parameter
    private String winMenuGroup;

    /**
     * 请求按用户安装
     */
    @Parameter(defaultValue = "false")
    private Boolean winPerUserInstall;

    /**
     * 请求为此应用程序创建桌面快捷方式
     */
    @Parameter(defaultValue = "false")
    private Boolean winShortcut;

    /**
     * 添加对话框，以便用户选择是否由安装程序创建快捷方式
     */
    @Parameter(defaultValue = "false")
    private Boolean winShortcutPrompt;

    /**
     * 用应用程序更新信息的URL
     */
    @Parameter
    private String winUpdateUrl;

    /**
     * 与此软件包升级相关联的UUID
     */
    @Parameter
    private String winUpgradeUuid;
    /**
     * Linux软件包的名称,默认为应用程序名称
     */
    @Parameter
    private String linuxPackageName;
    /**
     * .deb包的维护者 email-address
     */
    @Parameter
    private String linuxDebMaintainer;
    /**
     * 将此应用程序放置在的菜单组
     */
    @Parameter
    private String linuxMenuGroup;
    /**
     * 为应用程序创建快捷方式
     */
    @Parameter(defaultValue = "false")
    private Boolean linuxShortcut;

    @Override
    public void execute() throws MojoExecutionException, MojoFailureException {
        getLog().info("开始执行jfx:package...");
        init();
        DefaultExecutor executor = new DefaultExecutor();
        CommandLine command = buildCommand();
        try {
            executor.execute(command);
        } catch (IOException e) {
            throw new MojoExecutionException("指令执行失败", e);
        }
    }

    private void init() throws MojoExecutionException {
        clear();
        this.workDirectory = workDirectory();
        File resource = new File(project.getBasedir(), JAVAFX);
        initResourceDir(resource);
        if(StringUtils.isEmpty(imagePath)){
            copyLibrary();
        }
    }

    private void initResourceDir(File resource) {
        if (resource.exists()) {
            if (SystemUtils.IS_OS_WINDOWS) {
                File windows = new File(resource, WINDOWS);
                if (windows.exists()) {
                    resourceDirectory = windows.getAbsolutePath();
                }
            } else if (SystemUtils.IS_OS_MAC) {
                File mac = new File(resource, MAC);
                if (mac.exists()) {
                    resourceDirectory = mac.getAbsolutePath();
                }
            } else if (SystemUtils.IS_OS_LINUX) {
                File linux = new File(resource, LINUX);
                if (linux.exists()) {
                    resourceDirectory = linux.getAbsolutePath();
                }
            }
        }
    }

    public File path(String path) {
        File result = new File(path);
        if (result.exists()) {
            return result;
        }
        result = new File(project.getBasedir(), path);
        if (result.exists()) {
            return result;
        }
        return null;
    }

    private CommandLine buildCommand() {
        List<String> command = new ArrayList<>();
        command.add("jpackage");
        command.add("--dest");
        command.add("\"" + workDirectory + "\"");
        if (StringUtils.isNotEmpty(type)) {
            command.add("--type");
            command.add(type);
        }
        command.add("--app-version");
        command.add(appVersion);
        if (StringUtils.isNotEmpty(copyright)) {
            command.add("--copyright");
            command.add("\""+copyright+"\"");
        }
        if (StringUtils.isNotEmpty(description)) {
            command.add("--description");
            command.add(description);
        }
        if (StringUtils.isNotEmpty(icon)) {
            File path = path(icon);
            if (path != null) {
                command.add("--icon");
                command.add("\"" + path.getAbsolutePath() + "\"");
            } else {
                getLog().warn("icon文件不存在:" + icon);
            }
        }
        if (StringUtils.isEmpty(name)) {
            name = project.getName();
        }
        command.add("--name");
        command.add("\""+name+"\"");
        if (StringUtils.isNotEmpty(vendor)) {
            command.add("--vendor");
            command.add(vendor);
        }
        if (verbose) {
            command.add("--verbose");
        }
        if (StringUtils.isNotEmpty(aboutUrl)) {
            command.add("--about-url");
            command.add(aboutUrl);
        }
        if (StringUtils.isNotEmpty(installDir)) {
            command.add("--install-dir");
            command.add("\"" + installDir + "\"");
        }
        if (StringUtils.isNotEmpty(resourceDirectory)) {
            command.add("--resource-dir");
            command.add("\"" + resourceDirectory + "\"");
        }
        if(StringUtils.isEmpty(imagePath)){
            command.add("--input");
            command.add("\"" + new File(workDirectory, LIB).getAbsolutePath() + "\"");
            command.add("--main-class");
            command.add(mainClass);
            command.add("--main-jar");
            command.add("\"" + mainJar + "\"");
        }else{
            File image=path(imagePath);
            command.add("--runtime-image");
            command.add("\"" + image.getAbsolutePath() + "\"");
            command.add("--module");
            command.add(mainClass);
        }
        if (StringUtils.isEmpty(imagePath) && arguments != null && !arguments.isEmpty()) {
            for (String arg : arguments) {
                command.add("--arguments");
                command.add(arg);
            }
        }
        if (StringUtils.isEmpty(imagePath) && options != null && !options.isEmpty()) {
            options.forEach(option -> {
                command.add("--java-options");
                command.add("\"" + option + "\"");
            });
        }
        windows(command);
        mac(command);
        linux(command);
        getLog().info("执行指令:" + String.join(" ", command));
        return CommandLine.parse(String.join(" ", command));
    }

    private void linux(List<String> command) {
        if (!SystemUtils.IS_OS_LINUX) {
            return;
        }
        if (StringUtils.isNotEmpty(linuxPackageName)) {
            command.add("--linux-package-name");
            command.add(linuxPackageName);
        }
        if (StringUtils.isNotEmpty(linuxDebMaintainer)) {
            command.add("--linux-deb-maintainer");
            command.add(linuxDebMaintainer);
        }
        if (StringUtils.isNotEmpty(linuxMenuGroup)) {
            command.add("--linux-menu-group");
            command.add(linuxMenuGroup);
        }
        if (linuxShortcut) {
            command.add("--linux-shortcut");
        }
    }

    private void windows(List<String> command) {
        if (!SystemUtils.IS_OS_WINDOWS) {
            return;
        }
        if (winConsole) {
            command.add("--win-console");
        }
        if (winDirChooser) {
            command.add("--win-dir-chooser");
        }
        if (StringUtils.isNotEmpty(winHelpUrl)) {
            command.add("--win-help-url");
            command.add(winHelpUrl);
        }
        if (winMenu) {
            command.add("--win-menu");
        }
        if (StringUtils.isNotEmpty(winMenuGroup)) {
            command.add("--win-menu-group");
            command.add(winMenuGroup);
        }
        if (winPerUserInstall) {
            command.add("--win-per-user-install");
        }
        if (winShortcut) {
            command.add("--win-shortcut");
        }
        if (winShortcutPrompt) {
            command.add("--win-shortcut-prompt");
        }
        if (StringUtils.isNotEmpty(winUpdateUrl)) {
            command.add("--win-update-url");
            command.add(winUpdateUrl);
        }
        if (StringUtils.isNotEmpty(winUpgradeUuid)) {
            command.add("--win-upgrade-uuid");
            command.add(winUpgradeUuid);
        }
    }

    private void mac(List<String> command) {
        if (!SystemUtils.IS_OS_MAC) {
            return;
        }
        if (StringUtils.isNotEmpty(macPackageIdentifier)) {
            command.add("--mac-package-identifier");
            command.add(macPackageIdentifier);
        }
        if (StringUtils.isNotEmpty(macPackageName)) {
            command.add("--mac-package-name");
            command.add(macPackageName);
        }
        if (StringUtils.isNotEmpty(macPackageSigningPrefix)) {
            command.add("--mac-package-signing-prefix");
            command.add(macPackageSigningPrefix);
        }
        if (StringUtils.isNotEmpty(macSign)) {
            command.add("--mac-sign");
            command.add(macSign);
        }
        if (StringUtils.isNotEmpty(macSigningKeychain)) {
            command.add("--mac-signing-keychain");
            command.add(macSigningKeychain);
        }
        if (StringUtils.isNotEmpty(macSigningKeyUserName)) {
            command.add("--mac-signing-key-user-name");
            command.add(macSigningKeyUserName);
        }
        if (macAppStore) {
            command.add("--mac-app-store");
        }
        if (StringUtils.isNotEmpty(macEntitlements)) {
            File path = path(macEntitlements);
            if (path != null) {
                command.add("--mac-entitlements");
                command.add("\"" + path.getAbsolutePath() + "\"");
            } else {
                getLog().warn("mac-entitlements 文件不存在");
            }

        }
        if (StringUtils.isNotEmpty(macAppCategory)) {
            command.add("--mac-app-category");
            command.add(macAppCategory);
        }
    }

    private void copyLibrary() throws MojoExecutionException {
        File lib = mkdir(workDirectory, LIB);
        String directory = project.getBuild().getDirectory();
        String finalName = project.getBuild().getFinalName() + "." + project.getPackaging();
        Set<Artifact> artifacts = project.getArtifacts();
        for (Artifact artifact : artifacts) {
            File target = new File(lib, artifact.getFile().getName());
            getLog().info("正在复制文件:[" + artifact.getFile().getName() + "]至[" + target.getAbsolutePath() + "]");
            FileUtils.copy(artifact.getFile(), target);
        }
        File source = new File(directory, finalName);
        File target = new File(lib, finalName);
        getLog().info("正在复制文件:[" + finalName + "]至[" + target.getAbsolutePath() + "]");
        FileUtils.copy(source, target);
        mainJar = target.getAbsolutePath();
    }

    private void clear() throws MojoExecutionException {
        String directory = project.getBuild().getDirectory();
        File file = new File(directory, JAVAFX);
        FileUtils.remove(file);
    }

    public String workDirectory() throws MojoExecutionException {
        String directory = project.getBuild().getDirectory();
        File file = mkdir(directory, JAVAFX);
        return file.getAbsolutePath();
    }

    private File mkdir(String directory, String filename) throws MojoExecutionException {
        File file = new File(directory, filename);
        if (!file.exists()) {
            getLog().info("开始创建目录:[" + file.getAbsolutePath() + "]");
            if (!file.mkdirs()) {
                throw new MojoExecutionException("创建目录:[" + file.getAbsolutePath() + "]失败");
            }
        }
        return file;
    }
}
